package rpg.writer.scripts.impl;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import rpg.client.model.Actor;
import rpg.client.model.Dialog;
import rpg.client.model.DialogResponse;
import rpg.client.model.Position;
import rpg.client.model.Actor.Gender;
import rpg.engine.client.GameObjectInfo;
import rpg.engine.model.DialogInfo;
import rpg.engine.model.GameEffect;
import rpg.engine.model.GameMap;
import rpg.engine.model.MagicArea;
import rpg.engine.model.Mover;
import rpg.engine.model.When;
import rpg.engine.model.GameEffect.Type;
import rpg.util.Strings;
import rpg.writer.scripts.ScriptWarning;

public class ScriptBuffer {
    private final String storyID;
    private final Collection<ScriptWarning> warnings;

    final Map<String, GameMap.Builder> mapOfMaps = new LinkedHashMap<String, GameMap.Builder>();
    final Map<String, LinkedList<String>> mapsRows = new LinkedHashMap<String, LinkedList<String>>();

    private final Map<String, GameObjectInfo.Builder> mapOfObjects = new HashMap<String, GameObjectInfo.Builder>();

    private final Map<String, Actor.Builder> actors = new LinkedHashMap<String, Actor.Builder>();
    final Map<String, String> actorPlaces = new HashMap<String, String>();
    final Map<String, Position.Builder> mapActorPositions = new HashMap<String, Position.Builder>();

    private final Map<String, Dialog.Builder> mapOfDialogs = new HashMap<String, Dialog.Builder>();
    private final Map<String, DialogInfo.Builder> mapOfDialogInfos = new HashMap<String, DialogInfo.Builder>();

    private final List<GameEffect> startEffects = new ArrayList<GameEffect>();

    private final List<MagicArea> magicAreas = new ArrayList<MagicArea>();

    public ScriptBuffer(final String storyID, final Collection<ScriptWarning> warnings) {
        super();
        this.storyID = Strings.requireNonEmpty(storyID, "ParsedScript.storyID");
        this.warnings = warnings;
    }

    public void add(final MagicArea ma) {
        magicAreas.add(ma);
    }

    public void add(final ScriptWarning warning) {
        warnings.add(warning);
    }

    void add(String actorID, Actor.Builder builder) {
        Actor.Builder ab = actors.get(actorID);
        if (ab == null) {
            actors.put(actorID, builder);
        }
    }

    public void add(final String goID, final GameObjectInfo.Builder gob) {
        mapOfObjects.put(goID, gob);
    }

    public void add(final String currentMapID, final Mover m) {
        final GameMap.Builder builder = mapOfMaps.get(currentMapID);
        builder.mover(m);
    }

    void addDialogInfo(String chatID, Dialog.Builder db, DialogInfo.Builder dib) {
        mapOfDialogs.put(chatID, db);
        mapOfDialogInfos.put(chatID, dib);
    }

    void addEffectToLastDialog(final GameEffect ge, final String chatID) {
        if (ge == null) {
            return;
        }
        DialogInfo.Builder dib = mapOfDialogInfos.get(chatID);
        dib = dib.effect(ge);
    }

    public void addEffectToObject(final String gameObjectID, final GameEffect ge) {
        if (ge == null) {
            return;
        }
        GameObjectInfo.Builder gob = mapOfObjects.get(gameObjectID);
        gob.effect(ge);
    }

    public String addMap(final String name, final boolean firstScene) {
        final String mapName = Strings.trim(name);

        final GameMap.Builder builder = mapOfMaps.get(mapName);
        if (builder != null) {
            return mapName;
        }

        mapsRows.put(mapName, new LinkedList<String>());
        final GameMap.Builder b = new GameMap.Builder();
        b.id(mapName).name(mapName);

        mapOfMaps.put(mapName, b);

        if (firstScene) {
            final Position.Builder startPositionBuilder = new Position.Builder();
            startPositionBuilder.map(mapName);
            final Position startPosition = startPositionBuilder.build();

            GameEffect.Builder geb = new GameEffect.Builder();
            geb.type(Type.PLAYER_MAP_CHANGE);
            geb.to(startPosition);
            final GameEffect ge = geb.build();
            startEffects.add(ge);
        }

        return mapName;
    }

    public void addMapRow(final String currentMapID, final String line) {
        final LinkedList<String> lines = mapsRows.get(currentMapID);
        lines.addLast(line);
    }

    void addResponseToLastDialog(DialogResponse dr, String chatID) {
        Dialog.Builder db = mapOfDialogs.get(chatID);
        db.response(dr);
    }

    void addStartEffect(GameEffect ge) {
        if (ge == null) {
            return;
        }
        startEffects.add(ge);
    }

    void addWhensToLastDialog(List<When> whens, String chatID) {
        DialogInfo.Builder info = mapOfDialogInfos.get(chatID);
        for (When w : whens) {
            info.when(w);
        }
    }

    void addWhenToObject(final String gameObjectID, final When when) {
        GameObjectInfo.Builder gob = mapOfObjects.get(gameObjectID);
        gob = gob.when(when);
    }

    void firstPlace(String name, String currentMapID) {
        String mapID = actorPlaces.get(name);
        if (mapID == null) {
            actorPlaces.put(name, currentMapID);
        }
    }

    Collection<Actor.Builder> getActorBuilders() {
        return actors.values();
    }

    public String getInitialPosition(final String id) {
        return actorPlaces.get(id);
    }

    final List<MagicArea> getMagicAreas() {
        return magicAreas;
    }

    public Collection<GameObjectInfo> getObjects() {
        List<GameObjectInfo> gos = new ArrayList<GameObjectInfo>();

        for (GameObjectInfo.Builder gob : mapOfObjects.values()) {
            gos.add(gob.build());
        }

        return gos;
    }

    List<GameEffect> getStartEffects() {
        return startEffects;
    }

    public String getStoryID() {
        return storyID;
    }

    public Collection<ScriptWarning> getWarnings() {
        return warnings;
    }

    final List<DialogInfo> makeDialogInfo() {
        List<DialogInfo> list = new ArrayList<DialogInfo>();

        // Crunch Dialogs
        for (final Map.Entry<String, Dialog.Builder> entry : mapOfDialogs.entrySet()) {
            String chatID = entry.getKey();
            Dialog.Builder db = entry.getValue();
            DialogInfo.Builder dib = mapOfDialogInfos.get(chatID);

            dib.dialog(db.build());

            list.add(dib.build());
        }

        return list;
    }

    final List<GameMap> makeGameMaps() {
        final List<GameMap> maps = new ArrayList<GameMap>();

        // Crunch Maps
        for (final Map.Entry<String, GameMap.Builder> entry : mapOfMaps.entrySet()) {
            final String mapID = entry.getKey();
            final GameMap.Builder b = entry.getValue();

            final LinkedList<String> lines = mapsRows.get(mapID);
            if (lines.isEmpty()) {
                b.row(".....");
                b.row(".....");
                b.row(".....");
                b.row(".....");
                b.row(".....");
            }
            else {
                for (final String line : lines) {
                    b.row(line);
                }
            }

            // mapOfMaps.put(mapID, b.build());
        }

        return maps;
    }

    void setActorPosition(String actorID, Position.Builder pb) {
        mapActorPositions.put(actorID, pb);
    }

    void setGender(String actorID, Gender gender) {
        assert (gender != null);

        Actor.Builder ab = actors.get(actorID);
        if (ab == null) {
            ab = new Actor.Builder();
            ab.id(actorID);
        }

        ab.gender(gender);
    }

    void setHairColour(String actorID, Color c) {
        if (c == null) {
            return;
        }
        Actor.Builder ab = actors.get(actorID);
        ab.hairColour(c);
    }
}
